package com.example.window;

import com.example.beans.Event;
import com.example.source.ClickSource;
import org.apache.flink.api.common.eventtime.SerializableTimestampAssigner;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.ReduceFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;

import java.time.Duration;


/**
 *
 *  * @projectName myflinkstu
 *  * @title     TumblingEventTimeWindow_ReduceExample
 *  * @package    com.example.window
 *  * @description    滚动事件时间和使用窗口函数—归约函数ReduceFunction测试  reduce
 *  * @author hjx
 *  * @date   2022-3-28 14:45
 *  * @version V1.0.0
 *  * @copyright 2022 ty
 *
 */
public class TumblingEventTimeWindow_ReduceExample {

    public static void main(String[] args) {

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(2);

        // 获取数据源
        DataStreamSource<Event> dataStreamSource = env.addSource(new ClickSource(500L));

        SingleOutputStreamOperator<Event> streamWithWaterMark = dataStreamSource
                // 指定水位线生成器，插入水位线的逻辑
                .assignTimestampsAndWatermarks(
                        // 针对数据流插入水位线，延迟时间设置为 0 s  -> 相当于有序流
//                        WatermarkStrategy.<Event>forBoundedOutOfOrderness(Duration.ofSeconds(5))  // 针对乱序流插入水位线，延迟时间设置为 5s
                        WatermarkStrategy.<Event>forBoundedOutOfOrderness(Duration.ZERO)
                                // 获取事件的时间戳
                                .withTimestampAssigner(new SerializableTimestampAssigner<Event>() {
                                    @Override
                                    public long extractTimestamp(Event element, long recordTimestamp) {
                                        return element.timestamp;
                                    }
                                }));

        SingleOutputStreamOperator<Tuple2<String, Long>> result = streamWithWaterMark
                // 进行数据格式的转换，转换成元组 Tuple2(user,count)
                .map(new MapFunction<Event, Tuple2<String, Long>>() {
                    @Override
                    public Tuple2<String, Long> map(Event value) throws Exception {
                        return Tuple2.of(value.user, 1L);
                    }
                })
                // 按照 user 分组
                .keyBy(tupleData -> tupleData.f0)
                // 使用滚动事件时间窗口
                .window(TumblingProcessingTimeWindows.of(Time.seconds(5)))
                // 使用归约函数
                .reduce(new ReduceFunction<Tuple2<String, Long>>() {
                    @Override
                    public Tuple2<String, Long> reduce(Tuple2<String, Long> value1, Tuple2<String, Long> value2) throws Exception {
                        // // 定义累加规则，窗口闭合时，向下游发送累加结果
                        return Tuple2.of(value1.f0, value1.f1 + value2.f1);
                    }
                });

        dataStreamSource.print("dataStreamSource ");

        result.print("TumblingEventTimeWindow_ReduceExample ");

        try {
            env.execute();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
